pub mod sled_db;
pub mod error;

use config::Config;
use error::*;
use sled_db::*;

use std::collections::VecDeque;

use serde::{Deserialize, Serialize};

use crate::{container::ContainerId};

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum IndexConfig {
    sled(SledConfig),
}

impl IndexConfig {
    pub fn open<P: AsRef<std::path::Path>>(&self, path:P) -> IndexResult<IndexSourceSelector> {
        match self {
            IndexConfig::sled(config) => Ok(IndexSourceSelector::Sled(sled::open(path)?, config.clone())),
        }
    }
}

impl Default for IndexConfig {
    fn default() -> Self {
        Self::sled(SledConfig::default())
    }
}

pub enum IndexSourceSelector {
    Sled(sled::Db, SledConfig),
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FingerPrintChunkInfo {
    id_vec:VecDeque<ContainerId>,
}

impl FingerPrintChunkInfo {
    pub fn new() -> Self {
        Self { id_vec:VecDeque::new(), }
    }

    pub fn push_id_with_limit(&mut self, id:ContainerId, limit:usize) {
        if self.id_vec.len() == limit { self.id_vec.pop_front(); }
        self.id_vec.push_back(id);
    }

    pub fn id(&self) -> ContainerId {
        *self.id_vec.back().unwrap()
    }
}

pub trait FingePrintStore:Sized {
    fn insert_fp_with_container_id(&self, fp:&[u8], id:ContainerId) -> IndexResult<()>;
    fn get_container_id_from_fp(&self, fp:&[u8]) -> IndexResult<Option<ContainerId>>;
}

#[derive(Debug, Clone)]
#[impl_enum::with_methods{
    pub fn insert_fp_with_container_id(&self, fp:&[u8], id:ContainerId) -> IndexResult<()>
    pub fn get_container_id_from_fp(&self, fp:&[u8]) -> IndexResult<Option<ContainerId>>
}]
pub enum FingerPrintStoreSelector {
    Sled(SledFingerPrintStore),
}

impl FingerPrintStoreSelector {
    pub fn new(source:&IndexSourceSelector) -> IndexResult<Self> {
        match source {
            IndexSourceSelector::Sled(db, config) => Ok(Self::Sled(SledFingerPrintStore::open_tree(db, config.fp_limit)?)),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SuperFeatureChunkInfo {
    id_vec:VecDeque<ContainerId>,
}

impl SuperFeatureChunkInfo {
    pub fn new() -> Self {
        Self { id_vec:VecDeque::new(), }
    }

    pub fn push_id_with_limit(&mut self, id:ContainerId, limit:usize) {
        if self.id_vec.len() == limit { self.id_vec.pop_front(); }
        self.id_vec.push_back(id);
    }

    pub fn id(&self) -> ContainerId {
        *self.id_vec.back().unwrap()
    }
}


pub trait SuperFeatureStore:Sized {
    fn insert_sf_with_container_id(&self, sf:&[u64], id:ContainerId) -> IndexResult<()>;
    fn get_container_id_from_sf(&self, sf:&[u64]) -> IndexResult<Option<ContainerId>>;
}

#[derive(Debug, Clone)]
#[impl_enum::with_methods{
    fn insert_sf_with_container_id(&self, sf:&[u64], id:ContainerId) -> IndexResult<()>
    fn get_container_id_from_sf(&self, sf:&[u64]) -> IndexResult<Option<ContainerId>>
}]
pub enum SuperFeatureStoreSelector {
    Sled(SledSuperFeatureStore),
}

impl SuperFeatureStoreSelector {
    pub fn new(config:&Config, source:&IndexSourceSelector) -> IndexResult<Self> {
        let resemblance = config.get("resemblance").expect("no config for resemblance");
        match source {
            IndexSourceSelector::Sled(db, sled_config) 
                => Ok(Self::Sled(SledSuperFeatureStore::open_tree(db, sled_config.sf_limit, resemblance)?)),
        }
    }
}

#[cfg(test)]
fn test_finger_print_store<S:FingePrintStore>(store:S) -> IndexResult<()> {
    use crate::common::*;
    let fp_config = FingerPrintConfig::Sha1(Sha1Fp);
    let data_map_list = [
        (b"asscan".to_vec(), 1),
        (b"occnan".to_vec(), 2),
        (b"saclvana".to_vec(), 3),
        (b"abcasc".to_vec(), 4),
        (b"svsnvj".to_vec(), 5),
    ];
    let id_vec_list:Vec<_> = data_map_list.iter().map(|(_, id)|*id).collect();
    let fp_map_list:Vec<_> = data_map_list.iter()
        .map(|(data, id)|(fp_config.fp_of(data), *id))
        .collect();
    for (fp, id) in fp_map_list.iter() {
        store.insert_fp_with_container_id(&fp, *id)?;
    }
    let mut result_vec = Vec::new();
    for (fp, _) in fp_map_list.iter() {
        let id = store.get_container_id_from_fp(&fp)?.unwrap();
        result_vec.push(id);
    }
    assert_eq!(id_vec_list, result_vec);
    Ok(())
}


#[cfg(test)]
fn test_superfeature_store<S:SuperFeatureStore>(store:S, sf_config:crate::common::ResemblanceConfig) -> IndexResult<()> {
    let data_map_list = [
        (b"asscan".repeat(18), 1),
        (b"occnan".repeat(18), 2),
        (b"saclvana".repeat(19), 3),
        (b"abcasc".repeat(20), 4),
        (b"svsnvj".repeat(16), 5),
    ];
    let id_vec_list:Vec<_> = data_map_list.iter().map(|(_, id)|*id).collect();
    let sf_map_list:Vec<_> = data_map_list.iter()
        .map(|(data, id)|(sf_config.sf_of(data), *id))
        .collect();
    for (sf, id) in sf_map_list.iter() {
        store.insert_sf_with_container_id(sf, *id)?;
    }
    let mut result_vec = Vec::new();
    for (sf, _) in sf_map_list.iter() {
        let id = store.get_container_id_from_sf(sf)?.unwrap();
        result_vec.push(id);
    }
    assert_eq!(id_vec_list, result_vec);
    Ok(())
}